package com.zhongwang.mms.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Description: 文件处理工具类
 * @Author: China.sgl
 * @Date: 2019/12/9 12:48
 */
public class FileUtils {

    /**
     * 上传文件
     *
     * @param file       文件
     * @param uploadPath 路径
     * @return 文件名
     */
    public static String uploadGenDiyFileName(MultipartFile file, String uploadPath,String diyFileName) {
        if (!file.isEmpty()) {
            if (!mkdirs(uploadPath)) {
                return null;
            }
            File saveFile = new File(uploadPath + diyFileName);
            try {
                file.transferTo(saveFile);
            } catch (IOException e) {
                return null;
            }
            return diyFileName;
        } else {
            return null;
        }
    }
    /**
     * 上传文件
     *
     * @param file       文件
     * @param uploadPath 路径
     * @return 文件名
     */
    public static String upload(MultipartFile file, String uploadPath) {
        if (!file.isEmpty()) {
            String fileName = file.getOriginalFilename();
            if (!mkdirs(uploadPath)) {
                return null;
            }
            File saveFile = new File(uploadPath + fileName);
            try {
                file.transferTo(saveFile);
            } catch (IOException e) {
                return null;
            }
            return fileName;
        } else {
            return null;
        }
    }

    /**
     * 判断文件是否存在
     *
     * @param filePath 文件路径
     * @return boolean
     */
    public static boolean isExists(String filePath) {
        File file = new File(filePath);
        return file.exists();
    }

    /**
     * 判断是否为目录
     *
     * @param path 路径
     * @return boolean
     */
    public static boolean isDir(String path) {
        File file = new File(path);
        if (file.exists()) {
            return file.isDirectory();
        } else {
            return false;
        }
    }

    /**
     * 创建目录
     *
     * @param dirPath 路径
     * @return boolean
     */
    public static boolean mkdirs(String dirPath) {
        try {
            File file = new File(dirPath);
            if (!file.exists()) {
                boolean mkdir = file.mkdirs();
                if (!mkdir) {
                    return false;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名
     * @return boolean
     */
    public static boolean delete(String fileName) {
        try {
            File sourceFile = new File(fileName);
            if (sourceFile.isDirectory()) {
                for (File listFile : sourceFile.listFiles()) {
                    delete(listFile.getAbsolutePath());
                }
            }
            return sourceFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名
     * @return boolean
     */
    public static boolean remove(String fileName) {
        try {
            File sourceFile = new File(fileName);
            return sourceFile.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 保存图片 BASE64
     *
     * @param dataUrl url
     * @param path    path
     * @param imgName 文件名
     * @throws IOException IOException
     */
    public static void decodeBase64DataUrlToImage(String dataUrl, String path, String imgName) throws IOException {
        String base64 = dataUrl.substring(dataUrl.indexOf(",") + 1);
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        FileOutputStream write = new FileOutputStream(new File(path + imgName));
        byte[] decoderBytes = Base64.getDecoder().decode(base64);
        write.write(decoderBytes);
        write.close();
    }

    /**
     * 打包文件
     *
     * @param fileNames 文件集合
     */
    public static void zipFileAll(List<String> fileNames, HttpServletResponse response) {
        try {
            File file = new File("e:/download.zip");
            if (file.exists()) {
                file.delete();
            }
            file.createNewFile();
            //创建文件输出流
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            // ZipOutputStream输出流转换
            ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
            // 接收文件集合、压缩流
            for (String name : fileNames) {
                zipFile(name, zipOutputStream);
            }
            zipOutputStream.close();
            fileOutputStream.close();
            downloadZip(file, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 压缩文件
     *
     * @param fileName     文件名
     * @param outputStream 输出流
     */
    private static void zipFile(String fileName, ZipOutputStream outputStream) {
        try {
            File file = new File(fileName);
            if (file.exists()) {
                FileInputStream in = new FileInputStream(file);
                BufferedInputStream bins = new BufferedInputStream(in, 1024);
                // 将文件名写入压缩文件中
                ZipEntry entry = new ZipEntry(file.getName());
                outputStream.putNextEntry(entry);
                // 向压缩文件中输出数据
                int nNumber;
                byte[] buffer = new byte[1024];
                while ((nNumber = bins.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, nNumber);
                }
                outputStream.flush();
                bins.close();
                in.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 打包下载
     *
     * @param file     文件
     * @param response response
     */
    public static void downloadZip(File file, HttpServletResponse response) {
        downloadFile(file, response);
        try {
            File f = new File(file.getPath());
            f.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 下载文件
     *
     * @param fileName 文件名
     * @param response response
     */
    public static String downloadFile(String fileName, HttpServletResponse response) {
        File file = new File(fileName);
        return downloadFile(file, response);
    }

    /**
     * 下载文件
     *
     * @param file     文件
     * @param response response
     */
    public static String downloadFile(File file, HttpServletResponse response) {
        try {
            InputStream fis = new BufferedInputStream(new FileInputStream(file.getPath()));
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            response.reset();
            OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
            response.addHeader("Content-Length",String.valueOf(file.length()));
            toClient.write(buffer);
            toClient.flush();
            toClient.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return formatFileSize(file.length());
    }

    /**
     * 获取文件大小
     * @param sizeInBytes 文件大小单位字节
     * @return 文件大小字符串
     */
    public static String formatFileSize(long sizeInBytes) {
        final long KB = 1024;
        long MB = KB * 1024;
        long GB = MB * 1024;
       long TB = GB * 1024;
        if (sizeInBytes < KB) {
            return sizeInBytes + "B";
        } else if (sizeInBytes < MB) {
            return String.format("%.2fKB", (double) sizeInBytes / KB);
        } else if (sizeInBytes < GB) {
            return String.format("%.2fMB", (double) sizeInBytes / MB);
        } else if (sizeInBytes < TB) {
            return String.format("%.2fGB", (double) sizeInBytes / GB);
        } else {
            return String.format("%.2fTB", (double) sizeInBytes / TB);
        }
    }
    /**
     * excel 导出
     *
     * @param fileName  文件名
     * @param cls       导出模型类
     * @param sheetName sheet名
     * @param list      数据
     * @param response  response
     */
    public static void export(String fileName, Class cls, String sheetName, List list, HttpServletResponse response) {
        genExcel(fileName, cls, sheetName, list);
        FileUtils.downloadFile(fileName, response);
    }

    /**
     * excel 导出
     *
     * @param path      路径
     * @param fileName  文件名
     * @param cls       导出模型类
     * @param sheetName sheet名
     * @param list      数据
     * @param response  response
     */
    public static void export(String path, String fileName, Class cls, String sheetName, List list, HttpServletResponse response) {
        mkdirs(path);
        export(path + fileName, cls, sheetName, list, response);
    }

    /**
     * 生成excel
     *
     * @param path      路径
     * @param fileName  文件名
     * @param cls       导出模型类
     * @param sheetName sheet名
     * @param list      数据
     */
    public static void genExcel(String path, String fileName, Class cls, String sheetName, List list) {
        mkdirs(path);
        genExcel(path + fileName, cls, sheetName, list);
    }

    /**
     * 生成excel
     *
     * @param fileName  文件名
     * @param cls       导出模型类
     * @param sheetName sheet名
     * @param list      数据
     */
    public static void genExcel(String fileName, Class cls, String sheetName, List list) {
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 10);
        headWriteCellStyle.setWriteFont(headWriteFont);
        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, new WriteCellStyle());
        EasyExcel.write(fileName, cls).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName).doWrite(list);
    }


    public static void repeatedWrite(String fileName, Class cls, String sheetName, List list) {
        // 方法1 如果写到同一个sheet
        ExcelWriter excelWriter = null;
        try {
            // 这里 需要指定写用哪个class去写
            excelWriter = EasyExcel.write(fileName, cls).build();
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
            List<Object> childList = new ArrayList<Object>();
            for (int i = 0; i < list.size(); i++) {
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                childList.add(list.get(i));
                if (childList.size() <= 20000) {
                    excelWriter.write(childList, writeSheet);
                    childList = new ArrayList<Object>();
                }
            }
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

    }

    /**
     * 导入文件
     *
     * @param file     文件
     * @param cls      实体类型
     * @param listener 解析器
     * @throws IOException IO异常
     */
    public static void importExcel(MultipartFile file, Class cls, AnalysisEventListener listener) throws IOException {
        EasyExcel.read(file.getInputStream(), cls, listener).headRowNumber(1).sheet().doRead();
    }

    public static void decodeBase64DataURLToImage(String dataURL, String path, String imgName) throws IOException {
        String base64 = dataURL.substring(dataURL.indexOf(",") + 1);
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        FileOutputStream write = new FileOutputStream(new File(path + imgName));
        byte[] decoderBytes = Base64.getDecoder().decode(base64);
        write.write(decoderBytes);
        write.close();
    }

    /**
     *@name: copyFileUsingFileChannels
     *@description: 文件拷贝
     *@param: [source 源文件, dest 目标路径]
     *@returnType: void
     *@author: 徐一贺
     *@coddingDate: 2020/8/15 13:16
     *@version: v1.0
    **/
    public static void copyFileUsingFileChannels(File source, File dest) throws IOException {
        FileChannel inputChannel = null;
        FileChannel outputChannel = null;
        try {
            inputChannel = new FileInputStream(source).getChannel();
            outputChannel = new FileOutputStream(dest).getChannel();
            outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
        } finally {
            inputChannel.close();
            outputChannel.close();
        }
    }

    /**
     * 从 MultipartFile 中获取「文件名（不含扩展名）」
     * @param file 上传的文件对象
     * @return 不含扩展名的文件名（如 "test"），文件为空时返回 null
     */
    public static String getFileNameWithoutExtension(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return null;
        }
        String originalFilename = file.getOriginalFilename();
        return getFileNameWithoutExtension(originalFilename);
    }

    /**
     * 从完整文件名中获取「文件名（不含扩展名）」
     * @param originalFilename 完整文件名（如 "test.txt"、"C:\files\data.csv"、".gitignore"）
     * @return 不含扩展名的文件名
     */
    public static String getFileNameWithoutExtension(String originalFilename) {
        if (originalFilename == null || originalFilename.isEmpty()) {
            return "";
        }

        // 步骤1：处理文件名中的路径（提取纯文件名，兼容 Windows 和 Linux 路径）
        String fileName = originalFilename.replaceAll("\\\\|/", "/"); // 将 \ 替换为 /
        fileName = fileName.substring(fileName.lastIndexOf("/") + 1); // 截取最后一个 / 后的部分（纯文件名）

        // 步骤2：找到最后一个 . 的位置
        int lastDotIndex = fileName.lastIndexOf(".");

        // 步骤3：分割文件名（无扩展名时直接返回纯文件名）
        if (lastDotIndex == -1) { // 无扩展名（如 "README"、".gitignore" 但只有一个点）
            return fileName;
        }
        if (lastDotIndex == 0) { // 文件名以 . 开头（如 ".env"），视为无扩展名，返回原文件名
            return fileName;
        }
        // 正常情况：截取最后一个 . 之前的部分
        return fileName.substring(0, lastDotIndex);
    }

    /**
     * 从 MultipartFile 中获取「扩展名」（不含 .）
     * @param file 上传的文件对象
     * @return 扩展名（如 "txt"），无扩展名时返回空字符串，文件为空时返回 null
     */
    public static String getFileExtension(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return null;
        }
        String originalFilename = file.getOriginalFilename();
        return getFileExtension(originalFilename);
    }

    /**
     * 从完整文件名中获取「扩展名」（不含 .）
     * @param originalFilename 完整文件名（如 "test.txt"、"data.tar.gz"、"README"）
     * @return 扩展名（如 "txt"、"gz"），无扩展名时返回空字符串
     */
    public static String getFileExtension(String originalFilename) {
        if (originalFilename == null || originalFilename.isEmpty()) {
            return "";
        }

        // 步骤1：先提取纯文件名（处理路径）
        String fileName = originalFilename.replaceAll("\\\\|/", "/");
        fileName = fileName.substring(fileName.lastIndexOf("/") + 1);

        // 步骤2：找到最后一个 . 的位置
        int lastDotIndex = fileName.lastIndexOf(".");

        // 步骤3：提取扩展名（无 .）
        if (lastDotIndex == -1) { // 无扩展名（如 "README"）
            return "";
        }
        if (lastDotIndex == fileName.length() - 1) { // 文件名以 . 结尾（如 "test."），视为无扩展名
            return "";
        }
        // 正常情况：截取最后一个 . 之后的部分
        return fileName.substring(lastDotIndex + 1);
    }


}
